// Copyright (c) 2006 Zorm
// Released under the MIT License

#include "PCH.h"
#include "Common.h"

int Config::LoadConfig() {
	DWORD dwBytes = 0;
	char *fileBuf = 0;
	int line = 1;
    char szValueC[256] = "";
    DWORD dwValue = 0;
    int i = 0, nValuePointer = 0;
    int nState = CONFIG_NORMAL, nEscape = 0;
    
    tstringstream nameStream, valueStream;

	//open file
    HANDLE fp = CreateFile(m_configName.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
	if (!fp) {
		CloseHandle(fp);
		return -1;
	}

	//create buffer to read file
	DWORD fileSize = GetFileSize(fp, NULL);
	if (fileSize == INVALID_FILE_SIZE) {
		CloseHandle(fp);
		return -1;
	}

	//if its an empty file we are done here
	if (fileSize == 0) {
		CloseHandle(fp);
		return 0;
	}

    fileBuf = new char[fileSize];
    ZeroMemory(fileBuf, fileSize);

	//read file data
	if (!ReadFile(fp, fileBuf, fileSize, &dwBytes, NULL)) {
		CloseHandle(fp);
		return -1;
	}
    fileBuf[dwBytes-1] = 0;
    
	//close file as we are done with it
	CloseHandle(fp);

	//start processing the config file
    for (i = 0; fileBuf[i]; i++) {
		switch (nState) {
			case CONFIG_NORMAL: {
            	if (fileBuf[i] == '/' && fileBuf[i+1] == '/') {
                	nState = CONFIG_CPLUSPLUS_COMMENT;
                    i++;
                    continue;
                } else if (fileBuf[i] == '/' && fileBuf[i+1] == '*') {
                	nState = CONFIG_C_COMMENT;
                    i++;
                    continue;
                } else if (fileBuf[i] == '#') {
                	nState = CONFIG_BASH_COMMENT;
                    continue;
                } else if (fileBuf[i] == '"') {
                	nState = CONFIG_NAME;
                	nameStream.str(TEXT(""));
                	nameStream.clear();
                	valueStream.str(TEXT(""));
                	valueStream.clear();
                	nValuePointer = 0;
                    continue;
                } else if (fileBuf[i] == '\r' || fileBuf[i] == '\t' || fileBuf[i] == ' ') {
					continue;
                } else if (fileBuf[i] == '\n') {
					line++;
					continue;
				} else { //Unexcepted file input
					delete [] fileBuf;
					Clear();
					return line;
				}
				break;
			} 
			case CONFIG_CPLUSPLUS_COMMENT:
			case CONFIG_BASH_COMMENT: {
				if (fileBuf[i] == '\n') {
					line++;
                	nState = CONFIG_NORMAL;
                	continue;
				} else {
					continue;
				}
				break;
			} 
			case CONFIG_C_COMMENT: {
				if (fileBuf[i] == '\n') {
					line++;
					continue;
				} else if (fileBuf[i] == '*' && fileBuf[i+1] == '/') {
					i++;
					nState = CONFIG_NORMAL;
					continue;
				} else {
					continue;
				}
				break;
			}
			case CONFIG_NAME: {
				if (fileBuf[i] == '\\' && !nEscape) {
					nEscape = 1;
					continue;
				} else if ((fileBuf[i] == '"' || fileBuf[i] == '\\') && nEscape) {
					nEscape = 0;
					nameStream.put(fileBuf[i]);
					continue;
				} else if (fileBuf[i] == '"') {
					if (!(fileBuf[i+1] == ',' && fileBuf[i+2] == ' ')) {
						delete [] fileBuf;
						Clear();
						return line;
					}
					i += 2;
					nState = CONFIG_VALUE_START;
					continue;
				} else if (fileBuf[i] == '\r' || fileBuf[i] == '\n') {
					delete [] fileBuf;
					Clear();
					return line;
				} else {
					nameStream.put(fileBuf[i]);
					continue;
				}
				break;
			}
			case CONFIG_VALUE_START: {
				if (fileBuf[i] == '"') {
					nState = CONFIG_VALUE_STRING;
					continue;
				} else {
					nState = CONFIG_VALUE_NUMBER;
					i--;
					continue;
				}
				break;
			}
			case CONFIG_VALUE_STRING: {
				if (fileBuf[i] == '\\' && !nEscape) {
					nEscape = 1;
					continue;
				} else if ((fileBuf[i] == '"' || fileBuf[i] == '\\') && nEscape) {
 					nEscape = 0;
					valueStream.put(fileBuf[i]);
					continue;
				} else if (fileBuf[i] == '"') {
					AddConfigString(nameStream.str(), valueStream.str());
					nState = CONFIG_NORMAL;
					continue;
				} else if (fileBuf[i] == '\r' || fileBuf[i] == '\n') {
					delete [] fileBuf;
					Clear();
					return line;
                } else {
					valueStream.put(fileBuf[i]);
					continue;
				}
				break;
			}
			case CONFIG_VALUE_NUMBER: {
				if (fileBuf[i+1] == '"' || fileBuf[i+1] == ' ' || fileBuf[i+1] == '\t' || fileBuf[i+1] == '\n' || fileBuf[i+1] == '\r' || fileBuf[i+1] == '/' || fileBuf[i+1] == '#') {
                	DWORD dwBase = 10;
					char *psz;
					szValueC[nValuePointer] = fileBuf[i];
					szValueC[nValuePointer+1] = 0;
 					if(!_strnicmp(szValueC, "0x", 2)) {
						dwBase = 16;
						strcpy_s(szValueC, sizeof(szValueC), szValueC+2);
					} else if (szValueC[strlen(szValueC)-1] == 'h' || szValueC[strlen(szValueC)-1] == 'H') {
						dwBase = 16;
						szValueC[strlen(szValueC)-1] = 0;
					}
					dwValue = strtol(szValueC, &psz, dwBase);
					if (!dwValue && *psz) {
						nameStream.clear();
						valueStream.clear();
						delete [] fileBuf;
						Clear();
						return line;
					}
                   	AddConfigDWORD(nameStream.str(), dwValue);
					nState = CONFIG_NORMAL;
					continue;
				} else {
					szValueC[nValuePointer] = fileBuf[i];
					nValuePointer++;
					if (nValuePointer == 255) { //Value is to long
						delete [] fileBuf;
						Clear();
						return line;
					}
					continue;
				}
				break;
			}
		}
	}

	delete [] fileBuf;
	return 0;
}

void Config::AddConfigString(tstring name, tstring value) {
	m_stringMap[name] = value;
}

void Config::AddConfigDWORD(tstring name, DWORD dwValue) {
	m_dwordMap[name] = dwValue;
}

DWORD Config::GetDWORD(tstring name) {
	return m_dwordMap[name];
}

tstring Config::GetString(tstring name) {
	tstring value = m_stringMap[name];
	return value;
}

void Config::Clear() {
	
}

bool Config::Save() {
	TCHAR szTemp[1024] = TEXT("");
	char *szNumber = new char[35];
	DWORD dwWritten = 0;
	tstring tempConfigName = m_configName;
	tempConfigName.append(TEXT(".tmp"));

	HANDLE fp = CreateFile(tempConfigName.c_str(), GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
	if (!fp) {
		CloseHandle(fp);
		return false;
	}

	for(StringMapTypeIter iter = m_stringMap.begin(); iter != m_stringMap.end(); iter++) {
    	tstring name = (*iter).first;
		tstring value = (*iter).second;
		_sntprintf_s(szTemp, _countof(szTemp), _TRUNCATE, TEXT("\"%s\", \"%s\"%s"), name.c_str(), value.c_str(), TEXT("\r\n"));
		szTemp[1023] = 0;
		WriteFile(fp, szTemp, (DWORD)_tcslen(szTemp)*sizeof(TCHAR), &dwWritten, NULL);
	}

	for(DwordMapTypeIter iter = m_dwordMap.begin(); iter != m_dwordMap.end(); iter++) {
    	tstring name = (*iter).first;
		DWORD value = (*iter).second;
		tstringstream sstr;
		sstr << value;
		_sntprintf_s(szTemp, _countof(szTemp), _TRUNCATE, TEXT("\"%s\", %s%s"), name.c_str(), sstr.str().c_str(), TEXT("\r\n"));
		szTemp[1023] = 0;
		WriteFile(fp, szTemp, (DWORD)_tcslen(szTemp)*sizeof(TCHAR), &dwWritten, NULL);
	}
	delete [] szNumber;

	CloseHandle(fp);
	DeleteFile(m_configName.c_str());
	MoveFile(tempConfigName.c_str(), m_configName.c_str());
	
	return true;
}

void Config::LoadDefaults() {
	AddConfigString(TEXT("system.font"), TEXT("Tahoma"));
	AddConfigDWORD(TEXT("system.background"), 0x00000000);

	AddConfigDWORD(TEXT("system.color0"), 0x00FFFFFF);
	AddConfigDWORD(TEXT("system.color1"), 0x00C0C0C0);
	AddConfigDWORD(TEXT("system.color2"), 0x00734D26);
	AddConfigDWORD(TEXT("system.color3"), 0x004D7326);
	AddConfigDWORD(TEXT("system.color4"), 0x00262673);
	AddConfigDWORD(TEXT("system.color5"), 0x00267373);
	AddConfigDWORD(TEXT("system.color6"), 0x00732673);
	AddConfigDWORD(TEXT("system.color7"), 0x00737326);
	AddConfigDWORD(TEXT("system.color8"), 0x00A16B36);
	AddConfigDWORD(TEXT("system.color9"), 0x00732626);
	AddConfigDWORD(TEXT("system.color10"), 0x00267326);
	AddConfigDWORD(TEXT("system.color11"), 0x00264D73);
	AddConfigDWORD(TEXT("system.color12"), 0x004D2673);
	AddConfigDWORD(TEXT("system.color13"), 0x0073264D);
	AddConfigDWORD(TEXT("system.color14"), 0x00C48A4F);
}